package com.synergy.stockflow.utils;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.synergy.stockflow.constant.ResultCode;
import com.synergy.stockflow.exception.BusinessException;
import org.springframework.beans.BeanUtils;

import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

@SuppressWarnings("all")
public class ConvertUtils {
    /**
     * 将 MyBatis-Plus 的分页结果从一种类型转换为另一种类型。
     * <p>
     * 该方法用于在分页查询后，将原始的实体类（如 PO）分页对象转换为目标对象（如 VO）的分页对象，
     * 同时保留分页的元信息（当前页、页大小、总记录数等），仅转换数据列表部分。
     * </p>
     *
     * @param <T>       源数据类型，例如：数据库实体类（PO）
     * @param <R>       目标数据类型，例如：视图对象（VO）
     * @param page      源分页对象，包含原始数据列表和分页信息
     * @param converter 转换函数，用于将每个源对象 T 转换为目标对象 R
     *                  <p>例如:通过 BeanUtils.copyProperties 或自定义映射</p>
     * @return 转换后的目标类型分页对象，包含转换后的数据列表和原始分页信息
     * @example 示例：将 po Page<UserPO> 转换为 vo Page<UserVO>
     *
     *
     * <pre>
     * Page<UserVO> voPage = PageUtils.convert(poPage, po -> {
     *     UserVO vo = new UserVO();
     *     BeanUtils.copyProperties(po, vo);
     *     return vo;
     * });
     * </pre>
     */
    public static <T, R> Page<R> toPage(Page<T> page, Function<T, R> converter) {
        Page<R> result = new Page<>();
        result.setCurrent(page.getCurrent());
        result.setSize(page.getSize());
        result.setTotal(page.getTotal());
        result.setRecords(page.getRecords().stream().map(converter).collect(Collectors.toList()));
        return result;
    }

    /**
     * 将源对象列表转换为目标对象列表。
     * <p>
     * 该方法利用 Java 8 Stream 和函数式接口，对列表中的每个元素应用指定的转换函数，
     * 实现从一种类型到另一种类型的批量转换，常用于 PO 转 DTO、DTO 转 VO 等场景。
     * </p>
     *
     * @param <T>       源数据类型，例如：数据库实体类（PO）
     * @param <R>       目标数据类型，例如：视图对象（VO）或业务对象（BO）
     * @param list      源对象列表，不可为 null（若为空列表，应使用 Collections.emptyList()）
     * @param converter 转换函数，定义如何将一个 T 类型对象转换为 R 类型对象，
     *                  例如：userPo -> UserVoConverter.toVo(userPo) 或使用 MapStruct 映射
     * @return 转换后的目标类型对象列表，元素顺序与原列表一致；
     * 若输入列表为空，则返回空列表
     * @example <pre>
     * List<UserVO> voList = ConvertUtils.toList(poList, po -> {
     *     UserVO vo = new UserVO();
     *     BeanUtils.copyProperties(po, vo);
     *     vo.setLabel("用户");
     *     return vo;
     * });
     * </pre>
     */
    public static <T, R> List<R> toList(List<T> list, Function<T, R> converter) {
        return list.stream().map(converter).collect(Collectors.toList());
    }

    public static <T, R> List<R> toListSafe(List<T> list, Function<T, R> converter) {
        return list == null ? Collections.emptyList() :
                list.stream().map(converter).collect(Collectors.toList());
    }

    /**
     * 将单个源对象拷贝转换为目标类型的实例。
     * <p>
     * 该方法通过反射创建目标类的新实例，并使用 Spring 的 BeanUtils.copyProperties
     * 自动拷贝源对象与目标对象之间同名且类型兼容的属性值，适用于快速进行 PO/DTO/VO 之间的简单转换。
     * </p>
     * <p>
     * <strong>注意：</strong>此方法适用于字段名称一致、结构相似的对象转换。对于复杂映射（如嵌套对象、枚举、格式化字段），
     * 建议使用 MapStruct 等专业映射框架。
     * </p>
     *
     * @param <R>         目标对象的类型
     * @param source      源对象，可以为 null；若为 null，则返回 null
     * @param targetClass 目标类的 Class 对象，必须提供无参构造函数，否则实例化会失败
     * @return 转换后的目标对象实例，若 source 为 null 则返回 null
     * @throws BusinessException 当目标类无法实例化（如无无参构造函数）或属性拷贝失败时抛出业务异常
     * @example UserVO userVO = ConvertUtils.copy(userPO, UserVO.class);
     */
    public static <R> R copy(Object source, Class<R> targetClass) {
        if (source == null) return null;
        R target = null;
        try {
            target = targetClass.newInstance();
            BeanUtils.copyProperties(source, target);
        } catch (Exception e) {
            throw new BusinessException(ResultCode.BUSINESS_EXCEPTION, "对象转换失败：" + e.getMessage());
        }
        return target;
    }
}
